package db

import (
	"crypto/md5"
	"database/sql"
	"encoding/hex"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/go-redis/redis"
	_ "github.com/go-sql-driver/mysql"
)

var (
	redis_c   *redis.Client
	MysqlDB   *sql.DB
	RLogin_EX time.Duration = time.Second * 60 * 10
)

func db_ping() {
	var err error
	for {
		time.Sleep(time.Second * 30)
		if redis_c != nil {
			_, err := redis_c.Ping().Result()
			if err != nil {
				log.Println("Redis:", err)
			}
		}
		if MysqlDB != nil {
			err = MysqlDB.Ping()
			if err != nil {
				log.Println("mysql ping err!", err)
			}
		}
	}
}
func Init_DB() {
	var err error
	// redis db
	redis_c = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "tamigroup", // no password set
		DB:       0,           // use default DB
	})

	// init mysql
	MysqlDB, err = sql.Open("mysql", "tami:Tami1234!@tcp(127.0.0.1)/db_im?charset=utf8mb4&&multiStatements=true")
	if err != nil {
		log.Println("mysql", err)
	}
	MysqlDB.SetConnMaxLifetime(time.Second * 60 * 30) // 空闲30分钟左右断开
	MysqlDB.Exec("set names utf8mb4;")

	log.Println("DB init over")

	go db_ping()
}
func Session_Set(k string, v string) {
	if redis_c == nil {
		log.Println("Redis client is nil!")
		return
	}
	redis_c.Set(k, v, RLogin_EX)
}
func Session_Get(k string) (v string) {
	if redis_c == nil {
		log.Println("Redis client is nil!")
		return
	}
	var e error
	v, e = redis_c.Get(k).Result()
	if e != nil {
		v = ""
	}
	return
}
func Session_TTL_UP(k string) (nRet int32) {
	if redis_c == nil {
		log.Println("Redis client is nil!")
		return
	}
	v, _ := redis_c.TTL(k).Result()

	nRet = int32(v.Seconds())
	//log.Println(v, nRet)
	if nRet < 0 {
		return
	} else if nRet <= 60*3 {
		// update ttl
		redis_c.Expire(k, RLogin_EX)
		nRet = int32(RLogin_EX.Seconds())
	}
	//log.Println(v, nRet)
	return
}

func Session_TTL(k string) (v int32) {
	if redis_c == nil {
		log.Println("Redis client is nil!")
		return
	}
	t, e := redis_c.TTL(k).Result()
	if e != nil {
		v = -1
	} else {
		v = int32(t.Seconds())
	}
	return
}
func Session_DEL(k string) {
	if redis_c == nil {
		log.Println("Redis client is nil!")
		return
	}
	redis_c.Del(k).Result()
	return
}
func TextClear(src string) (dst string) {
	dst = src
	dst = strings.Replace(dst, "'", "\\'", -1)
	dst = strings.Replace(dst, "\"", "\\\"", -1)
	return
}

func SqlRows2Json(rs *sql.Rows) (retObj []map[string]interface{}) {
	columns, err := rs.Columns()
	if err != nil {
		return
	}
	count := len(columns)
	tableData := make([]map[string]interface{}, 0)
	values := make([]interface{}, count)
	valuePtrs := make([]interface{}, count)
	for rs.Next() {
		for i := 0; i < count; i++ {
			valuePtrs[i] = &values[i]
		}
		rs.Scan(valuePtrs...)
		entry := make(map[string]interface{})
		for i, col := range columns {
			var v interface{}
			val := values[i]
			b, ok := val.([]byte)
			if ok {
				v = string(b)
			} else {
				v = val
			}
			if val == nil {
				v = ""
			}
			entry[col] = v
		}
		tableData = append(tableData, entry)
	}
	retObj = tableData
	return
}

func Token_Make(id string, pwd string, salt string) (token string) {
	strTm := fmt.Sprint(time.Now().Unix())
	md5Maker := md5.New()
	md5Maker.Write([]byte(id + "#" + pwd))
	md5Maker.Write([]byte("#" + strTm + "#" + salt))
	token = hex.EncodeToString(md5Maker.Sum(nil))
	return
}

func User_logout(token string) (ok bool) {
	ok = false
	strID := Session_Get(token)
	//log.Println(strID)
	if len(strID) > 0 {
		Session_DEL(token)
		ok = true
		strSql := fmt.Sprintf("update t_users set `sessionid`='' where `id`=%v;", strID)
		rs, _ := MysqlDB.Query(strSql)
		if rs != nil {
			rs.Close()
		}
	}
	return
}

func User_login(id string, pwd string) (token string) {
	strSql := fmt.Sprintf("select * from t_users where `id`=%v and `pwd`='%v';",
		id, TextClear(pwd))
	rs, err := MysqlDB.Query(strSql)
	if err != nil {
		return
	}
	objRet := SqlRows2Json(rs)
	rs.Close()
	if len(objRet) > 0 {
		strSessionID := objRet[0]["sessionid"].(string)
		Session_DEL(strSessionID)
		//log.Println("Del", strSessionID)

		//nState := objRet[0]["state"].(string)
		//if nState == "1" { // disable state
		//	return
		//}
		// redis
		token = Token_Make(id, pwd, "Hello")
		Session_Set(token, id)
		strSql = fmt.Sprintf("update t_users set `sessionid`='%v' where `id`=%v;", token, id)
		rs, _ := MysqlDB.Query(strSql)
		if rs != nil {
			rs.Close()
		}
		return
	}
	return
}

func User_Info(id string) (retObj []map[string]interface{}) {
	// 1. check in mysql
	strSql := fmt.Sprintf("select * from t_userinfo where `uid`=%v;", id)
	rs, err := MysqlDB.Query(strSql)
	if err != nil {
		return
	}
	defer rs.Close()
	retObj = SqlRows2Json(rs)
	return
}
func User_Info2(uid string) (retObj []map[string]interface{}) {
	uInfo := User_Info(uid)[0]
	strList := strings.Split(uInfo["friends"].(string), ",")
	//log.Println(strList, ids)
	for _, oneF := range strList {
		strSql := fmt.Sprintf("select `uid`,`tm`,`type`,`name`,`desc`,`icon` from t_userinfo where `uid`=%v;", oneF)
		//log.Println(oneF, strSql)
		rs, err := MysqlDB.Query(strSql)
		if err != nil {
			return
		}
		retObj = append(retObj, SqlRows2Json(rs)...)
		rs.Close()
	}
	//log.Println(retObj)
	return
}

func User_Info3(uid string) (retObj []map[string]interface{}) {
	uInfo := User_Info(uid)[0]
	strList := strings.Split(uInfo["groups"].(string), ",")
	//log.Println(strList, ids)
	for _, oneF := range strList {
		strSql := fmt.Sprintf("select * from t_groups where `id`=%v;", oneF)
		//log.Println(oneF, strSql)
		rs, err := MysqlDB.Query(strSql)
		if err != nil {
			return
		}
		retObj = append(retObj, SqlRows2Json(rs)...)
		rs.Close()
	}
	//log.Println(retObj)
	return
}
